package com.alibaba.dubbo.common.configure;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class ExtensionConfiguratorUtils {
	ExtensionConfiguratorWraper configurator;
	
	
	static public ExtensionConfigurator getDefaultExtensionConfigurator(){
		return  ExtensionLoader.getExtensionLoader(ExtensionConfigurator.class).getExtension("default");
	}

	private  ExtensionConfiguratorUtils(){
		configurator = new ExtensionConfiguratorWraper();
		List<ExtensionConfigurator> configurators = new ArrayList<ExtensionConfigurator>();
		ExtensionLoader<ExtensionConfigurator> loader = ExtensionLoader.getExtensionLoader(ExtensionConfigurator.class);
		ExtensionConfigurator cfg = loader.getDefaultExtension();
		if(cfg  != null) {
			configurators.add(cfg);
		}
		try{
			cfg = loader.getAdaptiveExtension();
			if(cfg != null) configurators.add(cfg);
		}catch(Throwable t){
			
		}
		
		
		for(Map.Entry<String,Class<?>> entry: loader.getExtensionClasses().entrySet()){
			cfg = loader.getExtension(entry.getKey());
			if(cfg != null){
				configurators.add(cfg);
			}
		}
		
		//sort 
		Collections.sort(configurators, new Comparator<ExtensionConfigurator>(){

			@Override
			public int compare(ExtensionConfigurator l,
					ExtensionConfigurator r) {
				return l.order() - r.order();
			}
		});
		
		this.configurator.addExtensionConfigurator(configurators);
	}
	
	static class Holder{
		static ExtensionConfiguratorUtils INSTANCE;
		static {
			INSTANCE = new ExtensionConfiguratorUtils();
		}
	}
	
	
	
	class ExtensionConfiguratorWraper implements ExtensionConfigurator{
		
		List<ExtensionConfigurator> configurators = new ArrayList<ExtensionConfigurator>();
		
		public void addExtensionConfigurator(List<ExtensionConfigurator> cfgs){
			this.configurators.addAll(cfgs);
		}

		@Override
		public Object getValue(String name) {
			for(ExtensionConfigurator cfg : this.configurators){
				Object o = cfg.getValue(name);
				if(o != null) return o;
			}
			return null;
		}

		@Override
		public void setValue(String name, Object value) {
			for(ExtensionConfigurator cfg : this.configurators){
				cfg.setValue(name, value);
				break;
			}
		}

		@Override
		public boolean containsValue(String name) {
			for(ExtensionConfigurator cfg : this.configurators){
				if(cfg.containsValue(name)) return true;
			}
			return false;
		}

		@Override
		public Map<String, Object> getValues() {
			Map<String, Object> result = new HashMap<String, Object>();
			for(ExtensionConfigurator cfg : this.configurators){
				result.putAll(cfg.getValues());
			}
			return result;
		}

		@Override
		public void setValues(Map<String, Object> values) {
			for(ExtensionConfigurator cfg : this.configurators){
				cfg.setValues(values);
				break;
			}
		}

		@Override
		public int order() {
			return 0;
		}
	}
	
	public static ExtensionConfiguratorUtils getInstance(){
		return Holder.INSTANCE;
	}
	
	public int getIntValue(String name){
		return getIntValue(name, 0);
	}
	
	/**
	 * 
	 * @param cfg
	 * @param name
	 * @return
	 */
	public int getIntValue(String name, int defValue){
		if(!configurator.containsValue(name)) return defValue;
		Object value = configurator.getValue(name);
		if(value == null) return defValue;
		if(value instanceof Number) return ((Number)value).intValue();
		if(value instanceof String) return Integer.parseInt((String)value, 10);
		
		if(value instanceof Boolean) {
			return ((Boolean)value).booleanValue()?1:0;
		}
		
		return defValue;
	}
	
	public int getIntValue(String[] names, int defValue){
		for(String name : names){
			if(!configurator.containsValue(name)) continue;
			Object value = configurator.getValue(name);
			if(value == null) return defValue;
			if(value instanceof Number) return ((Number)value).intValue();
			if(value instanceof String) return Integer.parseInt((String)value, 10);
			
			if(value instanceof Boolean) {
				return ((Boolean)value).booleanValue()?1:0;
			}
		}
		
		return defValue;
	}
	
	/**
	 * 
	 * @param cfg
	 * @param name
	 * @return
	 */
	public long getLongValue(String name, long defValue){
		if(!configurator.containsValue(name)) return defValue;
		
		Object value = configurator.getValue(name);
		if(value == null) return defValue;
		if(value instanceof Number) return ((Number)value).longValue();
		if(value instanceof String) return Long.parseLong((String)value, 10);
		
		if(value instanceof Boolean) {
			return ((Boolean)value).booleanValue()?1L:0L;
		}
		
		return defValue;
	}
	
	public long getLongValue(String[] names, long defValue){
		for(String name : names){
			if(!configurator.containsValue(name)) continue;
			
			Object value = configurator.getValue(name);
			if(value == null) return defValue;
			if(value instanceof Number) return ((Number)value).longValue();
			if(value instanceof String) return Long.parseLong((String)value, 10);
			
			if(value instanceof Boolean) {
				return ((Boolean)value).booleanValue()?1L:0L;
			}
		}
		return defValue;
	}
	
	public long getLongValue(String name){
		return getLongValue(name, 0L);
	}
	
	public long getLongValue(String[] names){
		return getLongValue(names, 0L);
	}
	
	/**
	 * 
	 * @param cfg
	 * @param name
	 * @return
	 */
	public double getDoubleValue(String name, double defValue){
		if(!configurator.containsValue(name)) return defValue;
		
		Object value = configurator.getValue(name);
		if(value == null) return defValue;
		if(value instanceof Number) return ((Number)value).doubleValue();
		if(value instanceof String) return Double.parseDouble((String)value);
		
		if(value instanceof Boolean) {
			return ((Boolean)value).booleanValue()?1.0:0.0;
		}
		
		return defValue;
	}
	
	/**
	 * 
	 * @param cfg
	 * @param name
	 * @return
	 */
	public double getDoubleValue(String[] names, double defValue){
		for(String name : names){
			if(configurator.containsValue(name)) {
				Object value = configurator.getValue(name);
				if(value == null) return defValue;
				if(value instanceof Number) return ((Number)value).doubleValue();
				if(value instanceof String) return Double.parseDouble((String)value);
				
				if(value instanceof Boolean) {
					return ((Boolean)value).booleanValue()?1.0:0.0;
				}
			}
		}
		
		return defValue;
	}
	
	public double getDoubleValue(String name){
		return getDoubleValue(name, 0.0);
	}
	
	public boolean getBooleanValue(String name){
		return getBooleanValue(name, false);
	}
	
	
	public double getDoubleValue(String[] names){
		return getDoubleValue(names, 0.0);
	}
	
	public boolean getBooleanValue(String[] names){
		return getBooleanValue(names, false);
	}
	
	public Object getValue(String name){
		if(!configurator.containsValue(name)) return null;
		return configurator.getValue(name);
	}
	
	public Object getValue(String []names){
		for(String name : names){
			if(configurator.containsValue(name)){
				return configurator.getValue(name);
			}
		}
		return null;
	}
	
	
	
	/**
	 * 
	 * @param cfg
	 * @param name
	 * @return
	 */
	public boolean getBooleanValue(String name, boolean defValue){
		if(!configurator.containsValue(name)) return defValue;
		
		Object value = configurator.getValue(name);
		if(value == null) return defValue;
		if(value instanceof Number) return ((Number)value).longValue() == 0L?false:true;
		if(value instanceof String){
			String s = (String)value;
			return "true".equalsIgnoreCase(s)
					|| "yes".equalsIgnoreCase(s)
					|| "on".equalsIgnoreCase(s)
					|| "ok".equalsIgnoreCase(s);
		}
		
		if(value instanceof Boolean) {
			return ((Boolean)value).booleanValue();
		}
		
		return defValue;
	}
	
	/**
	 * 
	 * @param cfg
	 * @param name
	 * @return
	 */
	public boolean getBooleanValue(String[] names, boolean defValue){
		for(String name : names){
			if(configurator.containsValue(name)) {
				Object value = configurator.getValue(name);
				if(value == null) return defValue;
				if(value instanceof Number) return ((Number)value).longValue() == 0L?false:true;
				if(value instanceof String){
					String s = (String)value;
					return "true".equalsIgnoreCase(s)
							|| "yes".equalsIgnoreCase(s)
							|| "on".equalsIgnoreCase(s)
							|| "ok".equalsIgnoreCase(s);
				}
				
				if(value instanceof Boolean) {
					return ((Boolean)value).booleanValue();
				}
			}
		}
		return defValue;
	}
	
	public String getStringValue(String name){
		return getValue(name, String.class, null);
	}
	
	public String getStringValue(String[] names){
		return getValue(names, String.class, null);
	}
	
	public String getStringValue(String name, String defValue){
		return getValue(name, String.class, defValue);
	}
	
	public String getStringValue(String []names, String defValue){
		return getValue(names, String.class, defValue);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getValue(String name, Class<T> type, T defValue){
		if(!configurator.containsValue(name)) return defValue;
		Object value = configurator.getValue(name);
		if(value == null) return defValue;
		Class<?> vType = value.getClass();
		if(type == String.class){
			if(vType == String.class) return (T)value;
			return (T) value.toString();
		}
		if(vType == type || type.isAssignableFrom(vType)) return (T) value;
		return defValue;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getValue(String[] names, Class<T> type, T defValue){
		for(String name : names){
			if(configurator.containsValue(name)) {
				Object value = configurator.getValue(name);
				if(value == null) return defValue;
				Class<?> vType = value.getClass();
				if(type == String.class){
					if(vType == String.class) return (T)value;
					return (T) value.toString();
				}
				if(vType == type || type.isAssignableFrom(vType)) return (T) value;
			}
		}
		return defValue;
	}
	
	public <T> T getValue(String name, Class<T> type){
		return getValue(name, type, null);
	}
	
	public <T> T getValue(String[] names, Class<T> type){
		return getValue(names, type, null);
	}
	
	/**
	 * get class config value
	 * @param type
	 * @param defValue
	 * @return
	 */
	public <T> T getValue(Class<?> type, Class<T> target, T defValue){
		return this.<T>getValue(type.getName(), target, defValue);
	}
	
	public <T> T getValue(Class<?> type, Class<T> target){
		return getValue(type, target, null);
	}
	
	public <T> T getValue(Class<T> target){
		return getValue(target, target, null);
	}
	
	/**
	 * get cnfig value property of class
	 * @param type 
	 * @param property
	 * @param defValue
	 * @return
	 */
	public <T> T getValue(Class<?> type, String property, Class<T> target, T defValue){
		String name = combine(type, property);
		return this.<T>getValue(name, target, defValue);
	}
	
	public <T> T getValue(Class<?> type, String property, Class<T> target){
		return getValue(type, property, target, null);
	}
	
	/**
	 * combine keys
	 * @param names
	 * @return
	 */
	static public String combine(String ...names){
		StringBuffer sb = new StringBuffer();
		if(names != null && names.length > 0){
			for(String name : names){
				if(name != null && name.length() > 0){
					if(sb.length() > 0) sb.append(".");
					sb.append(name);
				}
			}
		}
		return sb.toString();
	}
	
	/**
	 * combine keys 
	 * @param names
	 * @return
	 */
	static public String combine(Class<?> type, String ...names){
		StringBuffer sb = new StringBuffer(type.getName());
		if(names != null && names.length > 0){
			for(String name : names){
				if(name != null && name.length() > 0){
					if(sb.length() > 0) sb.append(".");
					sb.append(name);
				}
			}
		}
		return sb.toString();
	}
}
